﻿using MbUnit.Framework;

namespace CommonBehavior
{
    [TestFixture]
    abstract public class WhenDeterminingEquality<T> 
    {
        /// <summary>
        /// x.Equals(x) returns true, except in cases that involve floating-point types. See IEC 60559:1989, Binary Floating-point Arithmetic for Microprocessor Systems.
        /// </summary>
        [Test]
        public void GivenThatAnInstanceIsComparedWithItselfTheResultShouldBeTrue()
        {
            // arrange
            T a = this.Instance;

            // act
            bool resultOfAEqualsA = a.Equals(a);

            // assert
            Assert.IsTrue(resultOfAEqualsA);
        }

        /// <summary>
        /// x.Equals(y) returns the same value as y.Equals(x).
        /// </summary>
        [Test]
        public void GivenThatXEqualsYThenYShouldEqualX()
        {
          // arrange
            T x = this.Instance;
            T y = this.Instance;

            bool resultOfXEqualsY = x.Equals(y);
            Assert.IsTrue(resultOfXEqualsY);

            // act 
            bool resultOfYEqualsX = y.Equals(x);

            // assert
            Assert.IsTrue(resultOfYEqualsX);
        }

        /// <summary>
        /// x.Equals(y) returns the same value as y.Equals(x).
        /// </summary>
        [Test]
        public void GivenThatXDoesNotEqualYThenYShouldNotEqualsX()
        {
            // arrange
            T x = this.Instance;
            T y = this.DifferentInstance;

            bool resultOfXEqualsY = x.Equals(y);
            Assert.IsFalse(resultOfXEqualsY);

            // act 
            bool resultOfYEqualsX = y.Equals(x);

            // assert
            Assert.IsFalse(resultOfYEqualsX);  
        }

        /// <summary>
        /// (x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.
        /// </summary>
        [Test]
        public void GivenThatXEqualsYAndYEqualsZThenXShouldEqualZ()
        {
            // arrange
            T x = this.Instance;
            T y = this.Instance;
            T z = this.Instance;

            bool resultOfXEqualsY = x.Equals(y);
            Assert.IsTrue(resultOfXEqualsY);

            bool resultOfYEqualsZ = y.Equals(z);
            Assert.IsTrue(resultOfYEqualsZ);

            // act
            bool resultOfXEqualsZ = x.Equals(z);

            // assert
            Assert.IsTrue(resultOfXEqualsZ);
        }

        /// <summary>
        /// (x.Equals(y) && y.Equals(z)) returns true if and only if x.Equals(z) returns true.
        /// </summary>
        [Test]
        public void GivenThatXEqualsYAndYNotEqualToZThenXShouldNotEqualZ()
        {
            // arrange
            T x = this.Instance;
            T y = this.Instance;
            T z = this.DifferentInstance;

            bool resultOfXEqualsY = x.Equals(y);
            Assert.IsTrue(resultOfXEqualsY);

            bool resultOfYEqualsZ = y.Equals(z);
            Assert.IsFalse(resultOfYEqualsZ);

            // act
            bool resultOfXEqualsZ = x.Equals(z);

            // assert
            Assert.IsFalse(resultOfXEqualsZ);
        }

        /// <summary>
        ///  x.Equals(nullNothingnullptra null reference (Nothing in Visual Basic)) returns false. 
        /// </summary>
        [Test]
        public void GivenThatObjectToCompareWithIsNullThenResultShouldBeFalse()
        {
            // arrange
            T x = this.Instance;

            // act
            bool resultofXEqualsNull = x.Equals(null);

            // assert
            Assert.IsFalse(resultofXEqualsNull);
        }

        /// <summary>
        /// Returns an intance.
        /// </summary>
        /// <returns></returns>
        protected abstract T Instance { get; }

        /// <summary>
        /// Returns an instance that is different from the first.
        /// </summary>
        /// <returns></returns>
        protected abstract T DifferentInstance { get; }

        #region Verifications that the implementation of XXXInstance is correct.

        [Test]
        public void InstanceAndDifferentInstanceShouldBeDifferent()
        {
            // arrange
            T instance = this.Instance;
            T other = this.DifferentInstance;

            // act
            bool resultOfInstanceEqualsOther = instance.Equals(other);

            // assert
            Assert.IsFalse(resultOfInstanceEqualsOther);
        }

        #endregion
    }
}
